import os
from dataclasses import dataclass
from pathlib import Path
from typing import IO

# 字符类型#[
CLANG_CHAR_TYPE_SPACE = 0  # 空格
CLANG_CHAR_TYPE_NAME = 1  # 变量名
CLANG_CHAR_TYPE_BRACKET = 2  # 括号
CLANG_CHAR_TYPE_OPERATOR = 3  # 运算符
CLANG_CHAR_TYPE_OTHER = 9  # 其它:;
CLANG_CHAR_TYPE_INV = -1  # 非法字符


def ClangCharType(s: str) -> int:
    if s.isspace():
        return CLANG_CHAR_TYPE_SPACE
    elif s.isalpha() or s.isdigit() or s == '_':
        return CLANG_CHAR_TYPE_NAME
    elif s in '()[]{}':
        return CLANG_CHAR_TYPE_BRACKET
    elif s in '+-*/=&|~^%!':
        return CLANG_CHAR_TYPE_OPERATOR
    elif s in ':;#':
        return CLANG_CHAR_TYPE_OTHER

    return CLANG_CHAR_TYPE_INV


class CFuncDec:
    sName: str = ''  # 原始函数名
    lArgs: list = None
    sRetType: str = ''
    tPos: tuple = (-1, -1)  # 起始/结束位置。结束位置以分号为准。

    sNameC: str = ''  # C函数名
    sFuncType:str=''#函数指针类型
    sMemberName: str = ''  # 函数指针的成员变量名

    def __init__(self):
        return

    def ArgsToStr(self, bType=True):
        ret = ''

        for i in range(len(self.lArgs)):
            if bType:
                ret += self.lArgs[i][0] + ' '
            ret += self.lArgs[i][1]
            if i < (len(self.lArgs) - 1):
                ret += ','

        return ret

    def Parse(self, sDec: str) -> str:
        iLeft = sDec.find('(')
        iRight = sDec.find(')')
        if iLeft < 0 or iRight < 0:
            return Exception('cannot find bracket')

        sTypeAndName = sDec[:iLeft]
        sArgs = sDec[iLeft + 1:iRight]

        # 函数名#[
        r = self.GetTypeName(sTypeAndName)
        if not isinstance(r, tuple):
            return Exception('cannot parse name')

        self.sRetType = r[0]
        self.sName = r[1]
        # ]
        # 参数#[
        self.lArgs = []
        lArgs = sArgs.split(',')
        for it in lArgs:
            r = self.GetTypeName(it)
            if not isinstance(r, tuple):
                return Exception('cannot parse args')
            self.lArgs.append(r)
        # ]
        return self.sName

    def GetTypeName(self, data) -> tuple:
        sType = ''

        if type(data) is list:
            iLen = len(data) - 1

            for i in range(iLen):
                sType += data[i] + ' '

            return (sType.strip(), data[iLen])

        elif type(data) is str:
            r = self.FindTags(data)
            if isinstance(r, BaseException):
                return r

            lData: list = r
            r = None
            for i in range(len(lData)):
                if lData[i][0] == '=':
                    r = i
                    break
            if r is not None:
                lData = lData[:i]

            return self.GetTypeName(lData)

        return

    def FindTags(self, sIn: str) -> list:
        ret = []
        iBegin = -1
        iType = -1

        for i in range(len(sIn)):
            r = ClangCharType(sIn[i])
            if r == CLANG_CHAR_TYPE_INV:
                return Exception('CLANG_CHAR_TYPE_INV')

            if iType >= 0:  # 已有头
                if r != iType:
                    ret.append(sIn[iBegin:i])
                    if r == CLANG_CHAR_TYPE_SPACE:
                        iType = -1
                        iBegin = -1
                    else:
                        iType = r
                        iBegin = i
            else:  # 没有头
                if r in (
                        CLANG_CHAR_TYPE_NAME, CLANG_CHAR_TYPE_BRACKET, CLANG_CHAR_TYPE_OPERATOR, CLANG_CHAR_TYPE_OTHER):
                    iBegin = i
                    iType = r

        if iType >= 0:
            ret.append(sIn[iBegin:])
        return ret


class CCppInterf:
    sAPI: str = 'API'  # 输入文件里的接口函数标识
    # sAPInit: str = 'API_INIT'  # 输入文件里的初始化函数标识

    tDllAPI: tuple = ('STDCALL','__stdcall')  # C接口头文件里的接口函数标识(宏名，宏值)

    sInputFile: str  # 输入文件的内容
    sOutputFile: str  # 输出文件的内容
    sClass: str
    sDllClass:str
    sFacClass:str

    lFunc: list = None

    def Load(self, sHeadFile: str, sClass: str) -> int:
        iRead = 0
        self.lFunc = []

        try:
            with open(sHeadFile, 'r') as oFile:
                self.sInputFile = oFile.read()
        except BaseException as e:
            return e

        r = self.FindTag(sClass)
        if r is None:
            return Exception('cannot find class')

        iRead = r + len(sClass)
        self.sClass = sClass

        while iRead < len(self.sInputFile):
            r = self.FindNextFunc(iRead)
            if isinstance(r, CFuncDec):
                self.lFunc.append(r)
                iRead = r.tPos[1] + 1
                print(f'Load: {r.sName}')
            else:
                break

        self.sClass = sClass
        return len(self.lFunc)

    def Make(self) -> int:

        # oHeadFileDll = self.CreateFile(Path(self.sClass + '_dll.h'))
        # oSrcFileDll = self.CreateFile(Path(self.sClass + '_dll.cpp'))
        # todo

        r=self.ClassName()

        for it in self.lFunc:
            oFunc: CFuncDec = it
            oFunc.sNameC=self.MethNameToFuncName(oFunc.sName)
            oFunc.sFuncType=self.MethNameToTpye(oFunc.sName)
            oFunc.sMemberName=self.MethNameToMemberName(oFunc.sName)

        r = self.WriteCH()
        print(f'WriteCH ret {r}')
        with open(self.sClass + '_api.h', 'w',encoding='utf-8') as f:
            f.write(self.sOutputFile)

        r = self.WriteCS()
        print(f'WriteCH ret {r}')
        with open(self.sClass + '_api.cpp', 'w',encoding='utf-8') as f:
            f.write(self.sOutputFile)

        r = self.WriteCppH()
        print(f'WriteCppH ret {r}')
        with open(self.sClass + '_dll.h', 'w',encoding='utf-8') as f:
            f.write(self.sOutputFile)

        r = self.WriteCppS()
        print(f'WriteCppS ret {r}')
        with open(self.sClass + '_dll.cpp', 'w',encoding='utf-8') as f:
            f.write(self.sOutputFile)

        return 0

    def WriteCH(self) -> int:  # C接口头文件
        self.sOutputFile = '#pragma once\n'
        self.WriteLine(f'#define {self.tDllAPI[0]} {self.tDllAPI[1]}')

        self.WriteLine('#ifdef __cplusplus')
        self.WriteLine('extern "C" {')
        self.WriteLine('#endif')

        for it in self.lFunc:
            oFunc: CFuncDec = it
            sDec = f'{oFunc.sRetType} {oFunc.sNameC}(HANDLE h,{oFunc.ArgsToStr()})'

            self.WriteLine(f'{self.tDllAPI[0]} {sDec};')

        self.WriteLine('#ifdef __cplusplus')
        self.WriteLine('}')
        self.WriteLine('#endif')
        return len(self.sOutputFile)

    def WriteCS(self) -> int:  # C接口源文件
        self.sOutputFile = f'#include "{self.sClass}_api.h"\n'
        for it in self.lFunc:
            oFunc: CFuncDec = it

            self.WriteLine(f'{oFunc.sRetType} {oFunc.sNameC}(HANDLE h,{oFunc.ArgsToStr()})')
            self.WriteLine('{')
            self.WriteLine(f'return (({self.sClass}*)h)->{oFunc.sName}({oFunc.ArgsToStr(False)});')
            self.WriteLine('}')
        return len(self.sOutputFile)

    def WriteCppH(self) -> int:  # C++接口头文件

        self.sOutputFile = '#pragma once\n'
        self.WriteLine(f'#define {self.tDllAPI[0]} {self.tDllAPI[1]}')
        self.WriteFuncType()

        self.WriteDllClass()
        self.WriteFacClass()
        return len(self.sOutputFile)

    def WriteCppS(self) -> int:  # C++接口源文件
        self.sOutputFile = f'#include "{self.sClass}_dll.h"\n'
        self.WriteDllFunc()
        self.WriteFacFunc()
        return 0

    def WriteDllFunc(self):
        self.WriteLine(f'{self.sDllClass}::{self.sDllClass}(HANDLE h,void *p)')
        self.WriteLine('{m_hData=h;m_pvParent=p;}')
        self.WriteLine(f'{self.sDllClass}::~{self.sDllClass}()')
        self.WriteLine('{//todo: close handle')
        self.WriteLine('}')

        for it in self.lFunc:
            oFunc: CFuncDec = it
            self.WriteLine(f'{oFunc.sRetType} {self.sDllClass}::{oFunc.sName}({oFunc.ArgsToStr()})')
            self.WriteLine('{')
            self.WriteLine((f'return (({self.sFacClass}*)m_pvParent)->{oFunc.sMemberName}({oFunc.ArgsToStr(False)});'))
            self.WriteLine('}')
        # todo
        return 0

    def WriteDllClass(self) -> int:
        self.WriteClassDec(self.sDllClass)
        self.WriteLine(f'{self.sDllClass}(HANDLE h,void *p);')
        self.WriteLine(f'~{self.sDllClass}();')

        for it in self.lFunc:
            oFunc: CFuncDec = it
            self.WriteLine(f'{oFunc.sRetType} {oFunc.sName}({oFunc.ArgsToStr()});')

        self.WriteLine('protected:')
        self.WriteLine('void* m_pvParent=NULL;')
        self.WriteLine('HANDLE m_hData=NULL;')

        self.WriteLine('};')
        return 0

    def WriteFacFunc(self):
        self.WriteLine(f'{self.sFacClass}::~{self.sFacClass}')
        self.WriteLine('{UNLoad();}')

        self.WriteToDoFunc(f'int {self.sFacClass}::LoadDll(char *pszFile)',-1)
        self.WriteToDoFunc(f'int {self.sFacClass}::UNLoad()',-1)
        self.WriteToDoFunc(f'void* {self.sFacClass}::GetFunc(char *pszName)','NULL')
        self.WriteToDoFunc(f'{self.sDllClass}* {self.sFacClass}::CreateIns()','NULL')
        #load#[
        self.WriteLine(f'int {self.sFacClass}::Load(char *pszFile)')
        self.WriteLine('{ int r;')
        self.WriteLine('r=LoadDll(pszFile);')
        self.WriteLine('if(r<0) return r;')

        for it in self.lFunc:
            oFunc: CFuncDec = it
            self.WriteLine(f'{oFunc.sMemberName}=({oFunc.sFuncType})GetFunc("{oFunc.sNameC}");')

        self.WriteLine('return 0;}')
        #]
        #check#[
        self.WriteLine(f'int {self.sFacClass}::CheckAPI()')
        self.WriteLine('{')

        for it in self.lFunc:
            oFunc: CFuncDec = it
            self.WriteLine(f'if({oFunc.sMemberName}==NULL) return -1;\n')

        self.WriteLine('return 0;}')
        #]

        return 0

    def WriteToDoFunc(self,sName:str,oRet):
        self.WriteLine('sName')
        self.WriteLine('{//todo')
        self.WriteLine(f'return {oRet};')
        self.WriteLine('}')


    def WriteFacClass(self) -> int:
        self.WriteClassDec(self.sFacClass)
        self.WriteOutput(f'{self.sFacClass}()')
        self.WriteLine('{};')
        self.WriteLine(f'~{self.sFacClass}();')

        self.WriteLine(f'int Load(char *pszFile);')
        self.WriteLine(f'int UNLoad();')
        self.WriteLine(f'{self.sDllClass}* CreateIns();')

        for it in self.lFunc:
            oFunc: CFuncDec = it
            self.WriteLine(f'{oFunc.sFuncType} {oFunc.sMemberName}=NULL;')

        self.WriteLine('protected:')
        self.WriteLine('HANDLE m_hDll=NULL;')
        self.WriteLine(f'int LoadDll(char *pszFile);//加载dll文件，为m_hDll赋值。')
        self.WriteLine(f'void* GetFunc(char *pszName);//根据函数名获取地址')
        self.WriteLine(f'int CheckAPI();//check func pointer. return 0 if no NULL, -1 if NULL exist.')

        self.WriteLine('};')
        return 0


    def WriteFuncType(self) -> int:

        for it in self.lFunc:
            oFunc: CFuncDec = it
            self.WriteLine(f'typedef {oFunc.sRetType} ({self.tDllAPI[0]}*{oFunc.sFuncType})({oFunc.ArgsToStr()});')
        return 0

    def WriteClassDec(self,sName):
        self.WriteLine(f'class {sName}')
        self.WriteLine('{')
        self.WriteLine('public:')
        return 0

    def WriteOutput(self, data) -> int:
        self.sOutputFile += data
        return len(data)

    def WriteLine(self, data) -> int:
        ret = self.WriteOutput(data)
        ret += self.WriteOutput('\n')
        return ret

    def FindTag(self, sTag: str, iStart: int = 0) -> int:
        iRead = iStart
        iLen = len(self.sInputFile)

        while iRead < iLen:
            r = self.sInputFile.find(sTag, iRead)
            if r < 0:
                return None

            if r == 0 or (self.sInputFile[r - 1] in ('\r\n ')):
                iNext = r + len(sTag)
                if iNext >= iLen or (self.sInputFile[iNext] in ('\r\n ')):
                    return r

            iRead = r + 1

        return None

    # 寻找匹配的括号
    def FindPair(self, iOffset: int) -> int:
        dPair = {'(': ')', '[': ']', '{': '}'}
        sLeft = self.sInputFile[iOffset]
        sRight = dPair.get(sLeft)
        if sRight is None:
            return Exception('char not supported')

        iDeep = 1
        iRead = iOffset
        while iRead < len(self.sInputFile):
            if self.sInputFile[iRead] == sLeft:
                iDeep += 1
            elif self.sInputFile[iRead] == sLeft:
                iDeep -= 1
                if iDeep <= 0:
                    return iRead

            iRead += 1
            # end while

        return -1

    def FindNextFunc(self, iStart: int) -> CFuncDec:
        oRet = CFuncDec()

        iTagStart = self.FindTag(self.sAPI, iStart)
        if type(iTagStart) is not int:
            return None
        iTagEnd = iTagStart + len(self.sAPI)

        iDecStart = iTagEnd + 1
        iDecEnd = self.sInputFile.find(';', iDecStart)
        if iDecEnd < 0:
            return None
        oRet.tPos = (iDecStart, iDecEnd)

        sDec = self.sInputFile[iDecStart:iDecEnd].strip()

        r = oRet.Parse(sDec)
        return oRet

    def MethNameToFuncName(self, sName) -> str:
        return sName

    def MethNameToTpye(self, sName) -> str:
        return 'FUNC_TYPE_' + sName

    def MethNameToMemberName(self, sName) -> str:
        return 'm_pfun' + sName

    def ClassName(self) -> tuple:
        # 将sClass转换为(调用类名,工厂类名)
        self.sDllClass='DC_' + self.sClass
        self.sFacClass='DF_' + self.sClass
        return (self.sDllClass,self.sFacClass)
